/****************************************************************************
 * Elisa 1.0                                                                *
 *--------------------------------------------------------------------------*
 * Author: Laurent Granvilliers, Vincent Sorin                              *
 * Copyright (c) 2004 Laboratoire d'Informatique de Nantes Atlantique       *
 *--------------------------------------------------------------------------*
 * Elisa is distributed WITHOUT ANY WARRANTY. Read the associated           *
 * COPYRIGHT file for more details.                                         *
 *--------------------------------------------------------------------------*
 * elisa_model.h                                                            *
 ****************************************************************************/

/**
 * @file elisa_model.h
 * @brief Base Classes for constraints model representation.
 */


#ifndef ELISA_CONSTRAINTS_MODEL_H
#define ELISA_CONSTRAINTS_MODEL_H

#include "elisa_defs.h"
#include "elisa_variable.h"
#include "elisa_constraint.h"
#include "elisa_reduction_factory.h"
#include <vector>

NAMESPACE_ELISA

/**
 * @ingroup Constraint
 * @class ModelRep
 * @brief Base class for constraints' model
 */
class ModelRep
{
public:
	/// Exception class
	class ReductionCreationException { };

	/**
 	 * Constructors
 	 */
	ModelRep()
	: m_nref(0)
	{
	}

	/**
 	 * Destructor
 	 */
	virtual ~ModelRep()
	{
	}

	/**
 	 * Writing on a stream
 	 * @param os the output stream for printing
 	 */
	virtual void print(std::ostream & os) const = 0;

	/**
 	 * Serialize the constraint in a Stream
 	 * @param o the output stream
 	 */
	virtual bool serialize(std::ostream & os) = 0;

	/**
 	 * Deserialize the constraint in a Stream
 	 * @param i the input stream
 	 */
	virtual bool deserialize(std::istream & i) = 0;

	/**
 	 * Compute the variable of the constraint in a set var
 	 * @param v the setvar where variables are putten
 	 */
	virtual void toVariables(VarImplSet & v) = 0;

	/**
 	 * @brief build the implementation of the variables in the searchtree st
 	 * @param st SearchTree
 	 */
	virtual void initVariableImplementation(SearchTree & searchTree) = 0;

	/**
 	 * Give the type of the model
 	 * @return a string that contains the name of the type
 	 */
	virtual std::string type() const = 0;

	/**
 	 * Build the set of reductions corresponding to the model
 	 * @param s : the setReduction
 	 */
	void createReductions(SetReduction & s)
	{
		ReductionFactory *f = ReductionFactoryChooser::get(this->type());
		if (f != 0) {
			f->create(this, s);
		} else {
			throw ReductionCreationException();
		}
	}

	/**
 	 * Build the reduction function corresponding to the model
 	 */
	virtual Reduction *createReduction() throw (ReductionCreationException)
	{
		ReductionFactory *f = ReductionFactoryChooser::get(this->type());
		if (f != 0) {
			return f->create(this);
		} else {
			throw ReductionCreationException();
		}
	}

	/// @name Management of reference counter
	//@{
	/// Increase the reference counter
	int addRef()
	{
		return ++m_nref;
	}

	/// Decrease the reference counter
	virtual	int delRef()
	{
		//  return --_nref;
		if (--m_nref == 0) {
			delete this;
		}
		return m_nref;
	}

	/// Return the value of the reference counter
	int ref() const
	{
		return m_nref;
	}
	//@}

protected:
	int m_nref;
};

/**
 * @class ConstraintHandler
 * class to handle a single constraint in a model.
 */
class ConstraintHandler : public ModelRep
{
public:
	ConstraintHandler(Constraint & c) 
	: ModelRep(),
	  m_constr(&c)
	{
	}

	/**
 	 * Writing on a stream
 	 * @param os the output stream for printing
 	 */
	void print(std::ostream & os) const
	{
		m_constr->print(os);
	}

	/**
 	 * Serialize the constraint in a Stream
 	 * @param o the output stream
 	 */
	bool serialize(std::ostream & os)
	{
		return m_constr->serialize(os);
	}

	/**
 	 * Deserialize the constraint in a Stream
 	 * @param i the input stream
 	 */
	bool deserialize(std::istream & i)
	{
		return m_constr->deserialize(i);
	}

	/**
 	 * Compute the variable of the constraint in a set var
 	 * @param v the setvar where variables are putten
 	 */
	void toVariables(VarImplSet & v)
	{
		m_constr->toVariables(v);
	}

	/**
 	 * @brief build the implementation of the variables in the searchtree st
 	 * @param st SearchTree
 	 */
	void initVariableImplementation(SearchTree & searchTree)
	{
		m_constr->initVariableImplementation(searchTree);
	}

	/**
 	 * Give the type of the model
 	 * @return a string that contains the name of the type
 	 */
	std::string type() const
	{
		return m_constr->type();
	}

	Constraint *getConstraint()
	{
		return m_constr;
	}

protected:
	Constraint *m_constr;
};


/**
 * @ingroup Constraint
 * @class Model
 * @brief Base class for constraints' model
 */
class Model
{
public:
	/**
 	 * Default constructor
 	 */
	Model()
	: m_model(0)
	{
	}

	/**
 	 * Constructor
 	 */
	Model(ModelRep & model)
	: m_model(&model)
	{
		if (m_model != 0) {
			m_model->addRef();
		}
	}

	Model(Constraint & c)
	: m_model(new ConstraintHandler(c))
	{
		m_model->addRef();
	}

	/**
 	 * Constructor
 	 */
	Model(ModelRep *model)
	: m_model(model)
	{
		if (m_model != 0) {
			m_model->addRef();
		}
	}

	/**
 	 * Destructor
 	 */
	~Model()
	{
		m_model->delRef();
	}

	/**
 	 * Copy operator
 	 */
	Model & operator=(const Model & model)
	{
		ModelRep *temp = m_model;
		m_model = model.model();
		if (m_model != 0) {
			m_model->addRef();
		}
		if (temp != 0) {
			temp->delRef();
		}
		return *this;
	}

	/**
 	 * Build the reduction function corresponding to the model
 	 */
	Reduction *createReduction() const
	{
		if (m_model != 0)
			return m_model->createReduction();
		else
			return 0;
	}

	/**
 	 * @brief build the implementation of the variables in the searchtree st
 	 * @param st SearchTree
 	 */
	void initVariableImplementation(SearchTree & searchTree) const
	{
		if (m_model != 0) {
			m_model->initVariableImplementation(searchTree);
		}
	}

	/**
 	 * Compute the variable of the constraint in a set var
 	 * @param v the set of vars where variables are put
 	 */
	void toVariables(VarImplSet & s) const
	{
		if (m_model != 0) {
			m_model->toVariables(s);
		}
	}

	/**
 	 * Update the symbolic domain of the variables with the computed value
 	 */
	void updateSymbolicDomain() const
	{
		VarImplSet s;
		this->toVariables(s);
		VarImplSet::iterator it;
		for (it = s.begin(); it != s.end(); it++) {
			(*it)->updateSymbolicDomain();
		}
	}

	/**
 	 * Give the type of the model
 	 * @return a string that contains the name of the type
 	 */
	std::string type() const
	{
		if (m_model != 0) {
			return m_model->type();
		}
	}

	/**
 	 * getter from the representation of the model
 	 */
	ModelRep *model() const
	{
		return m_model;
	}

	/**
 	 * setter from the representation of the model
 	 */
	void setModelRep(ModelRep *model)
	{
		m_model = model;
	}

	/**
 	 * Writing on a stream
 	 * @param os the output stream for printing
 	 */
	void print(std::ostream & os) const
	{
		if (m_model != 0) {
			m_model->print(os);
		}
	}

private:
	ModelRep *m_model;
};

END_NAMESPACE_ELISA

/**
 * @relates Model
 * print the model in a stream
 */
inline std::ostream & operator<<(std::ostream & os, const elisa::Model & m)
{
	m.print(os);
	return os;
}

#endif // ELISA_MODEL_H
